"""
执行器访问者

实现AST的执行功能，负责实际执行工作流中的各个节点。
"""

import asyncio
import time
from typing import Any, Dict, List, Optional, Union
from concurrent.futures import ThreadPoolExecutor, as_completed
from .base import BaseAstVisitor

# 前向声明，避免循环导入
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from ..ast.base import Ast, AstContext, AstNodeStatus
    from ..ast.nodes import *


class ExecutionResult:
    """执行结果"""
    
    def __init__(self):
        self.success: bool = True
        self.data: Any = None
        self.errors: List[str] = []
        self.warnings: List[str] = []
        self.execution_time: float = 0.0
        self.node_results: Dict[str, Any] = {}
        self.metadata: Dict[str, Any] = {}
    
    def add_error(self, error: str) -> None:
        """添加错误"""
        self.errors.append(error)
        self.success = False
    
    def add_warning(self, warning: str) -> None:
        """添加警告"""
        self.warnings.append(warning)
    
    def set_node_result(self, node_id: str, result: Any) -> None:
        """设置节点执行结果"""
        self.node_results[node_id] = result
    
    def get_node_result(self, node_id: str) -> Any:
        """获取节点执行结果"""
        return self.node_results.get(node_id)


class ExecutorVisitor(BaseAstVisitor):
    """
    执行器访问者
    
    负责执行AST中的各个节点，包括：
    - 管理执行状态
    - 处理并发执行
    - 错误处理和恢复
    - 进度跟踪
    """
    
    def __init__(self):
        super().__init__()
        self.execution_result = ExecutionResult()
        self.thread_pool: Optional[ThreadPoolExecutor] = None
        self.max_workers = 4
        self.timeout = 300  # 5分钟超时
        self.progress_callback = None
    
    async def execute(self, ast: 'Ast', context: 'AstContext') -> ExecutionResult:
        """异步执行AST"""
        start_time = time.time()
        
        try:
            # 重置执行结果
            self.execution_result = ExecutionResult()
            
            # 创建线程池
            self.thread_pool = ThreadPoolExecutor(max_workers=self.max_workers)
            
            # 执行AST
            result = await self._execute_async(ast, context)
            
            # 设置执行结果
            self.execution_result.data = result
            self.execution_result.execution_time = time.time() - start_time
            
        except Exception as e:
            self.execution_result.add_error(f"执行异常: {e}")
            self.execution_result.execution_time = time.time() - start_time
        finally:
            # 清理线程池
            if self.thread_pool:
                self.thread_pool.shutdown(wait=True)
                self.thread_pool = None
        
        return self.execution_result
    
    async def _execute_async(self, ast: 'Ast', context: 'AstContext') -> Any:
        """异步执行节点"""
        # 更新节点状态
        ast.status = self._get_node_status_enum().RUNNING
        ast.start_time = time.time()
        
        try:
            # 调用访问方法
            result = await self._visit_async(ast, context)
            
            # 更新状态为完成
            ast.status = self._get_node_status_enum().COMPLETED
            ast.end_time = time.time()
            
            # 保存结果
            self.execution_result.set_node_result(ast.node_id, result)
            
            # 更新进度
            if self.progress_callback:
                self.progress_callback(ast, result)
            
            return result
            
        except Exception as e:
            # 更新状态为失败
            ast.status = self._get_node_status_enum().FAILED
            ast.end_time = time.time()
            ast.error = e
            
            self.execution_result.add_error(f"节点 {ast.name} 执行失败: {e}")
            raise
    
    async def _visit_async(self, ast: 'Ast', context: 'AstContext') -> Any:
        """异步访问节点"""
        # 根据节点类型分发到对应的异步方法
        method_name = f"visit_{ast.node_type.value}_async"
        method = getattr(self, method_name, None)
        
        if method:
            return await method(ast, context)
        else:
            # 回退到同步方法
            return self.visit(ast, context)
    
    def _get_node_status_enum(self):
        """获取节点状态枚举（避免循环导入）"""
        from ..ast.base import AstNodeStatus
        return AstNodeStatus
    
    # ========================================================================
    # 基础控制节点的异步执行实现
    # ========================================================================
    
    async def visit_sequence_async(self, ast: 'Sequence', context: 'AstContext') -> List[Any]:
        """异步执行顺序节点"""
        results = []
        
        for child in ast.children:
            result = await self._execute_async(child, context)
            results.append(result)
            
            # 将结果传递给下一个节点
            context.set_data(f"prev_result", result)
        
        return results
    
    async def visit_parallel_async(self, ast: 'Parallel', context: 'AstContext') -> List[Any]:
        """异步执行并行节点"""
        # 创建任务列表
        tasks = []
        
        for child in ast.children:
            # 为每个子节点创建独立的上下文
            child_context = self._create_child_context(context)
            task = asyncio.create_task(self._execute_async(child, child_context))
            tasks.append(task)
        
        # 等待所有任务完成
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        # 处理异常结果
        processed_results = []
        for i, result in enumerate(results):
            if isinstance(result, Exception):
                self.execution_result.add_error(f"并行子任务 {i} 失败: {result}")
                processed_results.append(None)
            else:
                processed_results.append(result)
        
        return processed_results
    
    async def visit_conditional_async(self, ast: 'Conditional', context: 'AstContext') -> Any:
        """异步执行条件节点"""
        # 评估条件
        condition_result = await self._evaluate_condition_async(ast.condition, context)
        
        if condition_result and ast.true_branch:
            return await self._execute_async(ast.true_branch, context)
        elif not condition_result and ast.false_branch:
            return await self._execute_async(ast.false_branch, context)
        
        return None
    
    async def visit_loop_async(self, ast: 'Loop', context: 'AstContext') -> List[Any]:
        """异步执行循环节点"""
        results = []
        iteration = 0
        
        while True:
            # 检查最大迭代次数
            if ast.max_iterations and iteration >= ast.max_iterations:
                break
            
            # 检查循环条件
            if ast.condition:
                condition_result = await self._evaluate_condition_async(ast.condition, context)
                if not condition_result:
                    break
            
            # 执行子节点
            iteration_results = []
            for child in ast.children:
                result = await self._execute_async(child, context)
                iteration_results.append(result)
            
            results.append(iteration_results)
            iteration += 1
            
            # 更新迭代变量
            context.set_variable('iteration', iteration)
        
        return results
    
    # ========================================================================
    # 具体业务节点的异步执行实现
    # ========================================================================
    
    async def visit_ffmpeg_slice_async(self, ast: 'FfmpegSlice', context: 'AstContext') -> Dict[str, Any]:
        """异步执行FFmpeg切片节点"""
        # 这里应该调用实际的FFmpeg处理模块
        # 为了演示，我们使用模拟实现
        
        input_path = ast.input_path or context.get_data('input_path')
        if not input_path:
            raise ValueError("未指定输入视频路径")
        
        # 模拟异步处理
        await asyncio.sleep(1)  # 模拟处理时间
        
        # 模拟切片结果
        slices = [
            f"{ast.output_path}/slice_001.mp4",
            f"{ast.output_path}/slice_002.mp4",
            f"{ast.output_path}/slice_003.mp4",
        ]
        
        result = {
            "input_path": input_path,
            "output_slices": slices,
            "slice_count": len(slices),
            "total_duration": ast.max_duration * len(slices)
        }
        
        # 将切片列表传递给下一个节点
        context.set_data('video_slices', slices)
        
        return result
    
    async def visit_extract_metadata_async(self, ast: 'ExtractMetadata', context: 'AstContext') -> Dict[str, Any]:
        """异步执行元数据提取节点"""
        input_path = ast.input_path or context.get_data('input_path')
        if not input_path:
            raise ValueError("未指定输入视频路径")
        
        # 模拟异步处理
        await asyncio.sleep(0.5)
        
        # 模拟元数据结果
        metadata = {
            "duration": 120.5,
            "width": 1920,
            "height": 1080,
            "fps": 30,
            "codec": "h264",
            "bitrate": 5000000,
            "file_size": 50 * 1024 * 1024,  # 50MB
        }
        
        if ast.include_thumbnails:
            metadata["thumbnails"] = [
                f"{input_path}_thumb_{i}.jpg" for i in range(ast.thumbnail_count)
            ]
        
        # 将元数据传递给下一个节点
        context.set_data('video_metadata', metadata)
        
        return metadata
    
    async def visit_scan_directory_async(self, ast: 'ScanDirectory', context: 'AstContext') -> List[str]:
        """异步执行目录扫描节点"""
        import os
        import glob
        
        # 构建搜索模式
        patterns = []
        for ext in ast.extensions:
            if ast.recursive:
                pattern = os.path.join(ast.path, "**", f"*{ext}")
            else:
                pattern = os.path.join(ast.path, f"*{ext}")
            patterns.append(pattern)
        
        # 扫描文件
        files = []
        for pattern in patterns:
            found_files = glob.glob(pattern, recursive=ast.recursive)
            files.extend(found_files)
        
        # 应用模式过滤
        if ast.pattern:
            import re
            pattern_regex = re.compile(ast.pattern)
            files = [f for f in files if pattern_regex.search(os.path.basename(f))]
        
        # 将文件列表传递给下一个节点
        context.set_data('scanned_files', files)
        
        return files
    
    async def visit_gemini_classify_async(self, ast: 'GeminiClassify', context: 'AstContext') -> Dict[str, str]:
        """异步执行Gemini分类节点"""
        # 获取要分类的文件
        files = context.get_data('video_slices') or context.get_data('scanned_files', [])
        if not files:
            raise ValueError("没有找到要分类的文件")
        
        # 模拟异步AI分类
        classifications = {}
        for file_path in files:
            await asyncio.sleep(0.2)  # 模拟API调用时间
            
            # 模拟分类结果
            import random
            category = random.choice(ast.categories)
            classifications[file_path] = category
        
        # 将分类结果传递给下一个节点
        context.set_data('classifications', classifications)
        
        return classifications
    
    async def visit_organize_files_async(self, ast: 'OrganizeFiles', context: 'AstContext') -> Dict[str, List[str]]:
        """异步执行文件整理节点"""
        classifications = context.get_data('classifications', {})
        if not classifications:
            raise ValueError("没有找到分类结果")
        
        # 按类别组织文件
        organized = {}
        for file_path, category in classifications.items():
            if category not in organized:
                organized[category] = []
            organized[category].append(file_path)
        
        # 模拟文件移动操作
        if ast.move_files:
            await asyncio.sleep(0.5)  # 模拟文件操作时间
        
        return organized
    
    # ========================================================================
    # 辅助方法
    # ========================================================================
    
    def _create_child_context(self, parent_context: 'AstContext') -> 'AstContext':
        """为子节点创建独立的上下文"""
        from ..ast.base import AstContext
        
        child_context = AstContext()
        child_context.execution_id = parent_context.execution_id
        child_context.workspace_path = parent_context.workspace_path
        child_context.temp_path = parent_context.temp_path
        child_context.config = parent_context.config.copy()
        
        # 复制变量和数据
        child_context.variables = parent_context.variables.copy()
        child_context.data = parent_context.data.copy()
        
        return child_context
    
    async def _evaluate_condition_async(self, condition: str, context: 'AstContext') -> bool:
        """异步评估条件表达式"""
        # 这里可以实现更复杂的条件评估逻辑
        # 目前使用简化实现
        return self._evaluate_condition(condition, context)
    
    # ========================================================================
    # 同步方法的默认实现（用于不支持异步的节点）
    # ========================================================================
    
    def visit_output_draft(self, ast: 'OutputDraft', context: 'AstContext') -> Dict[str, Any]:
        """执行输出草稿节点"""
        # 模拟生成剪映草稿
        result = {
            "project_name": ast.project_name,
            "output_path": ast.output_path,
            "template_used": ast.template_path,
            "created_at": time.time()
        }
        
        return result
    
    def visit_generate_report(self, ast: 'GenerateReport', context: 'AstContext') -> Dict[str, Any]:
        """执行报告生成节点"""
        # 收集执行统计信息
        stats = {
            "total_files_processed": len(context.get_data('scanned_files', [])),
            "classifications": context.get_data('classifications', {}),
            "execution_time": self.execution_result.execution_time,
            "errors": self.execution_result.errors,
            "warnings": self.execution_result.warnings,
        }
        
        # 模拟生成报告文件
        result = {
            "report_path": ast.output_path,
            "format": ast.format,
            "statistics": stats if ast.include_statistics else None,
            "generated_at": time.time()
        }
        
        return result
    
    def visit_file_operation(self, ast: 'FileOperation', context: 'AstContext') -> Dict[str, Any]:
        """执行文件操作节点"""
        # 模拟文件操作
        result = {
            "operation": ast.operation,
            "source_path": ast.source_path,
            "target_path": ast.target_path,
            "success": True
        }
        
        return result
    
    def visit_data_transform(self, ast: 'DataTransform', context: 'AstContext') -> Any:
        """执行数据转换节点"""
        # 模拟数据转换
        input_data = ast.input_data or context.get_data('transform_input')
        
        # 根据转换类型处理数据
        if ast.transform_type == "json_to_dict":
            import json
            return json.loads(input_data) if isinstance(input_data, str) else input_data
        elif ast.transform_type == "dict_to_json":
            import json
            return json.dumps(input_data)
        else:
            return input_data
